'''
copyright: Copyright (C) 2015-2024, Wazuh Inc.

           Created by Wazuh, Inc. <info@wazuh.com>.

           This program is free software; you can redistribute it and/or modify it under the terms of GPLv2

type: integration

brief: Wazuh is able to detect vulnerabilities in the applications installed in agents using the Vulnerability Detector
       module. This software audit is performed through the integration of vulnerability feeds indexed by Redhat,
       Canonical, Debian, Amazon Linux and NVD Database.

components:
    - vulnerability_detector

suite: vulnerability_inventory

targets:
    - manager

daemons:
    - wazuh-modulesd

os_platform:
    - linux

os_version:
    - Arch Linux
    - Amazon Linux 2
    - Amazon Linux 1
    - CentOS 8
    - CentOS 7
    - Debian Buster
    - Red Hat 8
    - Ubuntu Focal
    - Ubuntu Bionic

references:
    - https://documentation.wazuh.com/current/user-manual/capabilities/vulnerability-detection/index.html
'''
import pytest
from pathlib import Path

from wazuh_testing.constants.daemons import ANALYSISD_DAEMON, MODULES_DAEMON, SYSCHECK_DAEMON
from wazuh_testing.constants.paths.logs import WAZUH_LOG_PATH
from wazuh_testing.utils.callbacks import generate_callback
from wazuh_testing.tools.monitors.file_monitor import FileMonitor
from wazuh_testing.utils.db_queries import agent_db
from wazuh_testing.utils.configuration import (get_test_cases_data, load_configuration_template,
                                               update_configuration_template)
from wazuh_testing.modules.modulesd.vulnerability_detector import patterns as cb
from wazuh_testing.modules.modulesd.configuration import MODULESD_DEBUG
from wazuh_testing.modules.monitord.configuration import MONITORD_ROTATE_LOG
from wazuh_testing.utils.mocking import VULNERABLE_PACKAGES
from test_vulnerability_detector import utils as evm
from . import (TEST_CASES_PATH, CONFIGURATIONS_PATH, custom_rhel_oval_feed_path, custom_rhel_json_feed_path,
               custom_nvd_json_feed_path)


pytest.skip("The tests will be deprecated, they test the old Vulnerability Detector.", allow_module_level=True)

# Variables
local_internal_options = {MODULESD_DEBUG: '2', MONITORD_ROTATE_LOG: '0'}
daemons_handler_configuration = {'daemons': [ANALYSISD_DAEMON, MODULES_DAEMON, SYSCHECK_DAEMON]}
pytestmark = [pytest.mark.server]

# Configuration and cases data
configurations_path = Path(CONFIGURATIONS_PATH, 'configuration_inventory_partial_scan.yaml')
t1_cases_path = Path(TEST_CASES_PATH, 'cases_vulnerability_inserted_partial_scan.yaml')
t2_cases_path = Path(TEST_CASES_PATH, 'cases_vulnerability_removed_partial_scan.yaml')

# test_vulnerability_inserted_partial_scan configurations
t1_configuration_parameters, t1_configuration_metadata, t1_case_ids = get_test_cases_data(t1_cases_path)
t1_configurations = load_configuration_template(configurations_path, t1_configuration_parameters,
                                                t1_configuration_metadata)
t1_systems = [metadata['system'] for metadata in t1_configuration_metadata]

# test_vulnerability_removed_partial_scan configurations
t2_configuration_parameters, t2_configuration_metadata, t2_case_ids = get_test_cases_data(t2_cases_path)
t2_configurations = load_configuration_template(configurations_path, t2_configuration_parameters,
                                                t2_configuration_metadata)
t2_systems = [metadata['system'] for metadata in t2_configuration_metadata]

# Set offline custom feeds configuration
t1_configurations = update_configuration_template(
    t1_configurations,  ['CUSTOM_REDHAT_OVAL_FEED', 'CUSTOM_REDHAT_JSON_FEED', 'CUSTOM_NVD_JSON_FEED'],
    [custom_rhel_oval_feed_path, custom_rhel_json_feed_path, custom_nvd_json_feed_path])
t2_configurations = update_configuration_template(
    t2_configurations,  ['CUSTOM_REDHAT_OVAL_FEED', 'CUSTOM_REDHAT_JSON_FEED', 'CUSTOM_NVD_JSON_FEED'],
    [custom_rhel_oval_feed_path, custom_rhel_json_feed_path, custom_nvd_json_feed_path])


@pytest.mark.tier(level=1)
@pytest.mark.parametrize('test_configuration, test_metadata, agent_system',
                         zip(t1_configurations, t1_configuration_metadata, t1_systems), ids=t1_case_ids)
def test_vulnerability_inserted_partial_scan(test_configuration, test_metadata, agent_system, set_wazuh_configuration,
                                             configure_local_internal_options, truncate_monitored_files,
                                             clean_cve_tables, prepare_partial_scan_with_vuln_packages,
                                             daemons_handler):
    '''
    description: Check that the partial scan inserts the detected vulnerabilities.

    test_phases:
        - setup:
            - Set a custom Wazuh configuration, with custom feeds for OVAL and NVD.
            - Configure custom local_internal_options.
            - Truncate wazuh logs.
            - Mock an agent with vulnerable packages.
            - Update sync_info packages data for that mocked agent.
            - Force a partial scan setting the last full scan DB data.
            - Restart wazuh-modulesd daemon to apply configuration changes.
        - test:
            - Check in the log that the partial scan starts.
            - Check in the log that the vulnerabilities have been detected.
            - Check in the agent database that the vulnerability has been inserted in the vuln_cves table (inventory).
        - teardown:
            - Truncate wazuh logs.
            - Restore initial configuration,and local_internal_options.
            - Restart modulesd to apply configuration.

    wazuh_min_version: 4.3.0

    tier: 1

    parameters:
        - test_configuration:
            type: dict
            brief: Wazuh configuration data. Needed for set_wazuh_configuration fixture.
        - test_metadata:
            type: dict
            brief: Wazuh configuration metadata
        - agent_system:
             type: str
             brief: System to set to the mocked agent.
        - set_wazuh_configuration:
            type: fixture
            brief: Set the wazuh configuration according to the configuration data.
        - configure_local_internal_options:
            type: fixture
            brief: Set local_internal_options configuration.
        - truncate_monitored_files:
            type: fixture
            brief: Truncate all the log files and json alerts files before and after the test execution.
        - clean_cve_tables:
            type: fixture
            brief: Clean all CVE tables.
        - prepare_partial_scan_with_vuln_packages:
            type: fixture
            brief: Setup the initial test state.
        - daemons_handler:
            type: fixture
            brief: Restart the wazuh-modulesd daemon.

    assertions:
        - Check that the partial scan log appears.
        - Check that the vulnerabilities are detected.
        - Check that vulnerabilities have been inserted in the vuln_cves inventory.

    input_description:
        - The `configuration_partial_vulnerability_inventory.yaml` file provides the module configuration for this test.
        - The `cases_partial_vulnerability_inserted` file provides the test cases.
    '''
    file_monitor = FileMonitor(WAZUH_LOG_PATH)
    agent_id = prepare_partial_scan_with_vuln_packages

    # Check (in log) that the partial scan has been launched
    log_present = evm.check_partial_scan_start(agent_id=agent_id)
    assert log_present is not None, f"No Partial scan start log found."

    # Check the vulnerability detection
    for package in VULNERABLE_PACKAGES:
        log_present = evm.check_cve_affects_package_log(agent_id, package['name'], package['cveid'])
        assert log_present is not None, f"Expected 'package {package['name']} is vulnerable to cve' log not found"

    # Check that the vulnerability has been inserted in the agent vulnerability inventory
    assert len(agent_db.get_vulnerability_inventory_data(agent_id=agent_id, status='VALID')) == \
        len(VULNERABLE_PACKAGES)


@pytest.mark.tier(level=1)
@pytest.mark.parametrize('test_configuration, test_metadata, agent_system',
                         zip(t2_configurations, t2_configuration_metadata, t2_systems), ids=t2_case_ids)
def test_vulnerability_removed_partial_scan(test_configuration, test_metadata, agent_system, set_wazuh_configuration,
                                            configure_local_internal_options, truncate_monitored_files,
                                            clean_cve_tables, prepare_partial_scan_with_obsolete_vulnerabilities,
                                            daemons_handler):
    '''
    description: Check that the partial scan removes the obsolete vulnerabilities from the inventory.

    test_phases:
        - setup:
            - Set a custom Wazuh configuration, with custom feeds for OVAL and NVD.
            - Configure custom local_internal_options.
            - Truncate wazuh logs.
            - Mock an agent with OBSOLETE vulnerabilities in the vuln_cves inventory.
            - Update sync_info packages data for that mocked agent.
            - Force a partial scan setting the last full scan DB data.
            - Restart wazuh-modulesd daemon to apply configuration changes.
        - test:
            - Check in the log that the partial scan starts.
            - Check in the log that the partial scan ends.
            - Check that the OBSOLETE vulnerabilities of the vuln_cves inventory have been removed.
        - teardown:
            - Truncate wazuh logs.
            - Restore initial configuration,and local_internal_options.
            - Restart modulesd to apply configuration.

    wazuh_min_version: 4.3.0

    tier: 1

    parameters:
        - test_configuration:
            type: dict
            brief: Wazuh configuration data. Needed for set_wazuh_configuration fixture.
        - test_metadata:
            type: dict
            brief: Wazuh configuration metadata
        - agent_system:
             type: str
             brief: System to set to the mocked agent.
        - set_wazuh_configuration:
            type: fixture
            brief: Set the wazuh configuration according to the configuration data.
        - configure_local_internal_options:
            type: fixture
            brief: Set local_internal_options configuration.
        - truncate_monitored_files:
            type: fixture
            brief: Truncate all the log files and json alerts files before and after the test execution.
        - clean_cve_tables:
            type: fixture
            brief: Clean all CVE tables.
        - prepare_partial_scan_with_obsolete_vulnerabilities:
            type: fixture
            brief: Setup the initial test state.
        - daemons_handler:
            type: fixture
            brief: Restart the wazuh-modulesd daemon.

    assertions:
        - Check that the partial scan log appears.
        - Check that the partial scan ends.
        - Check that the OBSOLETE vulnerabilities of the vuln_cves inventory have been removed.

    input_description:
        - The `configuration_partial_vulnerability_inventory.yaml` file provides the module configuration for this test.
        - The `cases_partial_vulnerability_removed` file provides the test cases.
    '''
    file_monitor = FileMonitor(WAZUH_LOG_PATH)
    agent_id = prepare_partial_scan_with_obsolete_vulnerabilities

    # Check that the vulnerabilities are in the inventory as OBSOLETE
    assert len(agent_db.get_vulnerability_inventory_data(agent_id=agent_id, status='OBSOLETE')) == \
        len(VULNERABLE_PACKAGES), 'The number of vulnerabilities in vuln_cves is not the expected one'

    # Check (in log) that the partial scan has been launched
    log_present = evm.check_partial_scan_start(agent_id=agent_id)
    assert log_present is not None, f"No Partial scan start log found."

    # Wait until vulnerability scan finishes
    file_monitor.start(callback=generate_callback(regex=cb.VULN_SCAN_FINISHED))
    assert file_monitor.callback_result is not None, f"No 'Vulnerability Scan Finished' log found."

    # Check that the OBSOLETE vulnerabilities have been removed
    assert len(agent_db.get_vulnerability_inventory_data(agent_id=agent_id)) == 0, \
        'The vulnerabilities inventory data has not been removed'
